// Last Update:2016-11-21 15:04:22
// 测试O_DIRECT和O_DSYNC

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <string>
#include <sstream>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::ostringstream;

#define WRITE_SIZE (1<<30)
#define MAX(a,b) ((a) > (b) ? (a):(b))

string get_random_buf(int len)
{
  string ret;
  string alphanum = "0123456789\n"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
    "abcdefghijklmnopqrstuvwxyz\n";

  for (int i = 0; i < len; ++i) {
    ret.push_back(alphanum[rand() % (alphanum.size() - 1)]);
  }

  return ret;
}

ssize_t safe_write(int fd, const void *buf, size_t count)
{
  while (count > 0) {
    ssize_t r = write(fd, buf, count);
    if (r < 0) {
      if (errno == EINTR)
        continue;
      return -errno;
    }
    count -= r;
    buf = (char *)buf + r;
  }
  return 0;
}

int main(int argc, const char *argv[])
{
  int fd, flags, ret;
  struct stat file_stat;
  unsigned page_size = sysconf(_SC_PAGESIZE);
  blksize_t blksize;
  blksize_t block_size;
  void *buff = NULL;

  string random_buf(get_random_buf(BUFSIZ));

  memset(&file_stat, 0, sizeof(file_stat));
  flags = O_RDWR | O_CREAT | O_DIRECT;// | O_DSYNC;

  fd = open("./a.txt", flags, 0644);
  if (fd == -1) {
    perror("oepn file error");
    exit(EXIT_FAILURE);
  }
  if (fstat(fd, &file_stat) != 0) {
    perror("fstat error");
    exit(EXIT_FAILURE);
  }
  blksize = file_stat.st_blksize;
  block_size = MAX(blksize, (blksize_t)page_size);
  ret = ::posix_memalign(&buff, block_size, BUFSIZ);
  if (ret != 0) {
    cerr << "posix_memalign error" << endl;
    goto err;
  }
  memmove(buff, random_buf.c_str(), BUFSIZ);

  while(true) {
    if (fstat(fd, &file_stat) != 0) {
      perror("fstat error");
      exit(EXIT_FAILURE);
    }
    if (file_stat.st_size >= WRITE_SIZE) {
      cout << "write finished\n";
      break;
    }
    if (safe_write(fd, buff, BUFSIZ) != 0) {
      perror("write error");
      ret = -2;
      break;
    }
  }
  free(buff);

err:
  close(fd);

  return ret;
}
